
/* Autores/ Authors:
Guillermo Rojas       guillermorojaslema@gmail.com
Claudio Acuña         claudioacunacuna@gmail.com
Cristian Garrido      cristian.garrido@ceinf.cl
*/

/*
Based on AdverseDeviant's work
Version: 0.9
Thank's to all that people who made possible this project
Gracias a toda esa gente que hizo posible este proyecto
*/

/**----------------------------------------------------------
Importaciones de clases
-----------------------------------------------------------*/
import SimpleOpenNI.*; /*Librería para conectar Kinect*/
import ddf.minim.*; /*Librería de sonido*/

import processing.serial.*; //proccesing serial es la libreria para usar el puerto serial con arduino
import cc.arduino.*; /*Importa arduino*/

/**----------------------------------------------------------
Variables.
-----------------------------------------------------------*/
// Valor vectorial para las manos.
PVector SKEL_LEFT_HAND = new PVector();
PVector SKEL_RIGHT_HAND = new PVector();

// Coordenadas XYZ para las manos.
float SKEL_LEFT_HANDX;
float SKEL_LEFT_HANDY;
float SKEL_LEFT_HANDZ;
float SKEL_RIGHT_HANDX;
float SKEL_RIGHT_HANDY;
float SKEL_RIGHT_HANDZ;


// Tamaño de los puntos dibujados en cada union
float dotSize = 30;

// De vector a un escalar de radio
float vectorScalar = 525;

// Variable de imagen
PImage img;

// Objeto kinect para interactuar con Kinect
SimpleOpenNI kinect;

// Variables de reproducción de sonido
Minim minim;
AudioPlayer player;

Arduino arduino;
int switchPin = 2;


// Valores booleanos para las manos en la batería
boolean leftTopDrum = false;
boolean rightTopDrum = false;
boolean rightBottomDrum = false;
boolean snareDrum = false;
boolean hihatDrum = false;
boolean leftCrashDrum = false;
boolean centerCrashDrum = false;
boolean rightCrashDrum = false;
boolean kickDrum = false;

/**----------------------------------------------------------
Método Setup. Prepara la kinect y dibuja una ventana. Carga una imagen y los componentes de audio.
-----------------------------------------------------------*/
void setup() 
{ 
  // Crea un nuevo objeto kinect
  kinect = new SimpleOpenNI(this); 
  // Efecto de espejo, para obtener una imagen más natural.
  kinect.setMirror(true); 
  // Habilita la generación de depthMap 
  kinect.enableDepth(); 
  // habilita el sensor RGB
  kinect.enableRGB();
  // Habilita la generación de esqueleto para los puntos
  kinect.enableUser(SimpleOpenNI.SKEL_PROFILE_ALL);
  
  // Crea un objeto arduino 
  arduino = new Arduino(this, Arduino.list()[0], 57600);
  // Prepara el bloque 2 en este caso para recibir impulsos
  arduino.pinMode(switchPin, Arduino.INPUT);

  // Crea una ventana del tamaño dela información de profundidad (depth)
  size(kinect.depthWidth(), kinect.depthHeight()); 
  // Color de fondo de la ventana,
  background(200,0,0);
  // El color para dibujar es rojo
  stroke(255,0,0);
  // Grosor de las lineas es suave 
  strokeWeight(1);
  // Sin suavidad
  smooth();
  
  // Carga la imagen de una batería genérica
  img = loadImage("drumset.png");
  // Carga los sonidos
  minim = new Minim(this);
} // void setup()

/**----------------------------------------------------------
Método de dibujar. Se repite por siempre. Actualiza la cámara 
de la kinecty dibuna una imagen en la ventana. Si Kinect está 
rastreando entonces captura las coordenadas de manos y las 
imprime. Revisa si ve que las manos del usuario están en el 
rango del set de batería.
-----------------------------------------------------------*/
void draw() 
{ 
  //Actualiza la cámara de Kinect
  kinect.update(); 
  //Dibuja una imagen rgb en las coordenadas (0,0)
  image(kinect.rgbImage(),0,0);
  //Dibuja una batería en las coordenadas (100,200)
  image(img,100,200);
  
  // Si Kinect está rastreando al usuario 1
  if (kinect.isTrackingSkeleton(1)) 
  { 
    getCoordinates();
    printHandCoordinates();
    // Revisa que cada tambor para ver si las manos está en proximidad
    leftTopDrum();
    rightTopDrum();
    rightBottomDrum();
    snareDrum();
    hihatDrum();
    leftCrashDrum();
    centerCrashDrum();
    rightCrashDrum();
    kickDrum();
  } // Si el rastreo del esqueleto funciona, une los puntos
} // void draw() 

/**----------------------------------------------------------
Llamado cuando un nuevo usuario es encontrado. Imprime un nuevo ID e usuario
Y comienza la detencción de pose si el rastreo de esqueleto no está habilitado.
Si lo está entonces termina el método. Input es "int user ID" de un nuevo usuario.
-----------------------------------------------------------*/
void onNewUser(int userId) 
{ 
  println("onNewUser - userId: " + userId); 
  
  if (kinect.isTrackingSkeleton(1)) 
    return; 
    
  println(" start pose detection"); 
  kinect.startPoseDetection("Psi", userId);
  
} // void onNewUser(int userId)

/**----------------------------------------------------------
Llamado cuando un usuario se ha perdido. Imprime el ID del usuario perdido
Input es "int userId" del usuario perdido.
-----------------------------------------------------------*/
void onLostUser(int userId) 
{ 
  println("onLostUser - userId: " + userId); 
}

/**----------------------------------------------------------
Llamado cuando la posicionamiento ha sido detectada. Se detiene la detección y
solicita un calibración de esqueleto. Input es String de la posición detectada
e int "user ID".
-----------------------------------------------------------*/
void onStartPose(String pose, int userId) 
{ 
  println("onStartPose - userId: " + userId + ", pose: " + pose); 
  println(" stop pose detection"); 
  kinect.stopPoseDetection(userId); 
  kinect.requestCalibrationSkeleton(userId, true); 
}

/**----------------------------------------------------------
Llamado cuando un posicionamiento ha terminado.
Input es String de la posición detectada e int "user ID".
-----------------------------------------------------------*/
void onEndPose(String pose, int userId)
{ 
  println("onEndPose - userId: " + userId + ", pose: " + pose); 
} 

/**----------------------------------------------------------
Llamado cuando la calibración de esqueleto comienza. Input es "int user ID".
-----------------------------------------------------------*/
void onStartCalibration(int userId) 
{ 
  println("onStartCalibration - userId: " + userId); 
} 

/**----------------------------------------------------------
Llamado cuando la calibración de esqueleto termina. Si la calibración
ha sido exitosa, entonces comienza a rastrear el esqueleto, de lo contrario
comience la detección de posicionamiento de nuevo. Input es "int user ID and booleano
si el usuario ha sido con éxito calibrado.
-----------------------------------------------------------*/
void onEndCalibration(int userId, boolean successfull)
{ 
  println("onEndCalibration - userId: " + userId + ", successfull: " + successfull); 
  if (successfull) 
  { 
    println(" User calibrated !!!"); 
    kinect.startTrackingSkeleton(userId); 
  } 
  else 
  { 
    println(" Failed to calibrate user !!!"); 
    println(" Start pose detection"); 
    kinect.startPoseDetection("Psi", userId); 
  } 
}

/**----------------------------------------------------------
Obtiene las coordenadas XYZ de las manos rastreadas.
-----------------------------------------------------------*/ 
void getCoordinates() 
{
  // Obtiene la posicion de las manos
  kinect.getJointPositionSkeleton(1,SimpleOpenNI.SKEL_LEFT_HAND,SKEL_LEFT_HAND);
  kinect.getJointPositionSkeleton(1,SimpleOpenNI.SKEL_RIGHT_HAND,SKEL_RIGHT_HAND);

  // Convierte a punto de mundo real a espacio proyectivo
  kinect.convertRealWorldToProjective(SKEL_LEFT_HAND,SKEL_LEFT_HAND);
  kinect.convertRealWorldToProjective(SKEL_RIGHT_HAND,SKEL_RIGHT_HAND);

  // Escala el vector z de cada punto a forma escalar
  SKEL_LEFT_HANDX = (vectorScalar/SKEL_LEFT_HAND.x);
  SKEL_RIGHT_HANDX = (vectorScalar/SKEL_RIGHT_HAND.x);
  SKEL_LEFT_HANDY = (vectorScalar/SKEL_LEFT_HAND.y);
  SKEL_RIGHT_HANDY = (vectorScalar/SKEL_RIGHT_HAND.y);
  SKEL_LEFT_HANDZ = (vectorScalar/SKEL_LEFT_HAND.z);
  SKEL_RIGHT_HANDZ = (vectorScalar/SKEL_RIGHT_HAND.z);
  
  // Rellenea el punto como rojo.
  fill(255,0,0); 
  ellipse(SKEL_LEFT_HAND.x,SKEL_LEFT_HAND.y, SKEL_LEFT_HANDZ*dotSize,SKEL_LEFT_HANDZ*dotSize);
  ellipse(SKEL_RIGHT_HAND.x,SKEL_RIGHT_HAND.y, SKEL_RIGHT_HANDZ*dotSize,SKEL_RIGHT_HANDZ*dotSize);
} // void getCoordinates()

/*-----------------------------------------------------------
Imprime las coordenadas XYZ en el monitor serial. Falta debuggeo
-----------------------------------------------------------*/
void printHandCoordinates()
{
  println("Left hand XYZ: " + SKEL_LEFT_HAND.x + " " + SKEL_LEFT_HAND.y + " "
          + SKEL_LEFT_HAND.z);
  println("right hand XYZ: " + SKEL_RIGHT_HAND.x + " " + SKEL_RIGHT_HAND.y + " "
          + SKEL_RIGHT_HAND.z);
} // void getHandCoordinates()

/**----------------------------------------------------------
Si las manos del usuario son rastreadas en el area del tomb izquierdo, entonces toque un sonido.
-----------------------------------------------------------*/
void leftTopDrum()
{
  if((SKEL_LEFT_HAND.x > 250 & SKEL_LEFT_HAND.x < 260
    & SKEL_LEFT_HAND.y > 280 & SKEL_LEFT_HAND.y < 290 
    & leftTopDrum == false) 
    | (SKEL_RIGHT_HAND.x > 250 & SKEL_RIGHT_HAND.x < 260
    & SKEL_RIGHT_HAND.y > 280 & SKEL_RIGHT_HAND.y < 290 
    & leftTopDrum == false))
    {
      // cargue el sonido
      player = minim.loadFile("leftTopDrum.wav");
      // reproduzca el archivo
      player.play();
      leftTopDrum = true;
    }
    else
    {
      leftTopDrum = false;
    }
} // void leftTopDrum()

/**----------------------------------------------------------
Si las manos del usuario son rastreadas en el area del tomb de arriba derecho,
entonces toque un sonido.
-----------------------------------------------------------*/
void rightTopDrum()
{
  if((SKEL_LEFT_HAND.x > 325 & SKEL_LEFT_HAND.x < 335
    & SKEL_LEFT_HAND.y > 285 & SKEL_LEFT_HAND.y < 295
    & rightTopDrum == false) 
    | (SKEL_RIGHT_HAND.x > 325 & SKEL_RIGHT_HAND.x < 335
    & SKEL_RIGHT_HAND.y > 285 & SKEL_RIGHT_HAND.y < 295 
    & rightTopDrum == false))
    {
      // cargue el sonido
      player = minim.loadFile("rightTopDrum.wav");
      // reproduzca el archivo
      player.play();
      rightTopDrum = true;
    }
    else
    {
      rightTopDrum = false;
    }
} // void rightTopDrum()

/**----------------------------------------------------------
Si las manos del usuario son rastreadas en el area del tomb de abajo derecho,
entonces toque un sonido.
-----------------------------------------------------------*/
void rightBottomDrum()
{
  if((SKEL_LEFT_HAND.x > 385 & SKEL_LEFT_HAND.x < 395
    & SKEL_LEFT_HAND.y > 340 & SKEL_LEFT_HAND.y < 350
    & rightBottomDrum == false) 
    | (SKEL_RIGHT_HAND.x > 385 & SKEL_RIGHT_HAND.x <395
    & SKEL_RIGHT_HAND.y > 340 & SKEL_RIGHT_HAND.y < 350 
    & rightBottomDrum == false))
    {
      // cargue el sonido
      player = minim.loadFile("rightBottomDrum.wav");
      // reproduzca el archivo
      player.play();
      rightBottomDrum = true;
    }
    else
    {
      rightBottomDrum = false;
    }
} // void rightBottomDrum()

/**----------------------------------------------------------
Si las manos del usuario son rastreadas en el area de la caja
entonces toque un sonido.
-----------------------------------------------------------*/
void snareDrum()
{
  if((SKEL_LEFT_HAND.x > 215 & SKEL_LEFT_HAND.x < 225
    & SKEL_LEFT_HAND.y > 325 & SKEL_LEFT_HAND.y < 335
    & snareDrum == false) 
    | (SKEL_RIGHT_HAND.x > 215 & SKEL_RIGHT_HAND.x < 225
    & SKEL_RIGHT_HAND.y > 325 & SKEL_RIGHT_HAND.y < 335 
    & snareDrum == false))
    {
      // cargue el sonido
      player = minim.loadFile("snareDrum.wav");
      // reproduzca el archivo
      player.play();
      snareDrum = true;
    }
    else
    {
      snareDrum = false;
    }
} // void snareDrum()

/**----------------------------------------------------------
Si las manos del usuario son rastreadas en el area del hihat,
entonces toque un sonido.
-----------------------------------------------------------*/
void hihatDrum()
{
  if((SKEL_LEFT_HAND.x > 145 & SKEL_LEFT_HAND.x < 155
    & SKEL_LEFT_HAND.y > 275 & SKEL_LEFT_HAND.y < 285
    & hihatDrum == false) 
    | (SKEL_RIGHT_HAND.x > 145 & SKEL_RIGHT_HAND.x < 155
    & SKEL_RIGHT_HAND.y > 275 & SKEL_RIGHT_HAND.y < 285 
    & hihatDrum == false))
    {
      // cargue el sonido
      player = minim.loadFile("hihatDrum.wav");
      // reproduzca el archivo
      player.play();
      hihatDrum = true;
    }
    else
    {
      hihatDrum = false;
    }
} // void hihatDrum()

/**----------------------------------------------------------
Si las manos del usuario son rastreadas en el area del crash izquierdo
entonces toque un sonido.
-----------------------------------------------------------*/
void leftCrashDrum()
{
  if((SKEL_LEFT_HAND.x > 210 & SKEL_LEFT_HAND.x < 220
    & SKEL_LEFT_HAND.y > 220 & SKEL_LEFT_HAND.y < 230
    & leftCrashDrum == false) 
    | (SKEL_RIGHT_HAND.x > 210 & SKEL_RIGHT_HAND.x < 220
    & SKEL_RIGHT_HAND.y > 220 & SKEL_RIGHT_HAND.y < 230 
    & leftCrashDrum == false))
    {
      // cargue el sonido
      player = minim.loadFile("leftCrashDrum.wav");
      // reproduzca el archivo
      player.play();
      leftCrashDrum = true;
    }
    else
    {
      leftCrashDrum = false;
    }
} // void leftCrashDrum()

/**----------------------------------------------------------
Si las manos del usuario son rastreadas en el area del crash central,
entonces toque un sonido.
-----------------------------------------------------------*/
void centerCrashDrum()
{
  if((SKEL_LEFT_HAND.x > 280 & SKEL_LEFT_HAND.x < 290
    & SKEL_LEFT_HAND.y > 220 & SKEL_LEFT_HAND.y < 230
    & centerCrashDrum == false) 
    | (SKEL_RIGHT_HAND.x > 280 & SKEL_RIGHT_HAND.x < 290
    & SKEL_RIGHT_HAND.y > 220 & SKEL_RIGHT_HAND.y < 230 
    & centerCrashDrum == false))
    {
      // cargue el sonido
      player = minim.loadFile("centerCrashDrum.wav");
      // reproduzca el archivo
      player.play();
      centerCrashDrum = true;
    }
    else
    {
      centerCrashDrum = false;
    }
} // void centerCrashDrum()

/**----------------------------------------------------------
Si las manos del usuario son rastreadas en el area del crash derecho,
entonces toque un sonido.
-----------------------------------------------------------*/
void rightCrashDrum()
{
  if((SKEL_LEFT_HAND.x > 395 & SKEL_LEFT_HAND.x < 405
    & SKEL_LEFT_HAND.y > 225 & SKEL_LEFT_HAND.y < 235
    & rightCrashDrum == false) 
    | (SKEL_RIGHT_HAND.x > 395 & SKEL_RIGHT_HAND.x < 405
    & SKEL_RIGHT_HAND.y > 225 & SKEL_RIGHT_HAND.y < 235 
    & rightCrashDrum == false))
    {
      // cargue sonido
      player = minim.loadFile("rightCrashDrum.wav");
      // reproduzca el archivo
      player.play();
      rightCrashDrum = true;
    }
    else
    {
      rightCrashDrum = false;
    }
} // void rightCrashDrum()

/**----------------------------------------------------------
Si el usuario presiona el botón conetado a Arduino, 
entonces toque un sonido
-----------------------------------------------------------*/
void kickDrum()
{
  if(arduino.digitalRead(switchPin)==Arduino.LOW)
    {
        kickDrum = false;
      // cargue el sonido

    }
    else
    {
          player = minim.loadFile("kickDrum.wav");
      // reproduzca el archivo
      player.play();
      kickDrum = true;
    }
} // void kickDrum()



